package com.wuyangnju.healthclub.dao.util;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class GenericDaoJpa<K, E> implements GenericDao<K, E> {

	private Log log = LogFactory.getLog(getClass());

	public void create(EntityManager em, E entity) {
		log.info("create " + getEntityClass() + " : " + entity);
		em.persist(entity);
	}

	public List<E> retrieve(EntityManager em) {
		log.info("retrieve " + getEntityClass() + " : " + "all");
		return getQuery(em).getResultList();
	}

	public E retrieve(EntityManager em, K id) {
		log.info("retrieve " + getEntityClass() + " : " + id);
		return em.find(getEntityClass(), id);
	}

	@SuppressWarnings("unchecked")
	public void update(EntityManager em, K id, E entity) {
		log.info("update " + getEntityClass() + " : " + id);
		E entityToUpdate = retrieve(em, id);
		if (entityToUpdate == null) {
			return;
		}
		if (entityToUpdate instanceof PutAble<?>) {
			PutAble<E> c = (PutAble<E>) entity;
			c.copy(entityToUpdate);
			em.merge(entityToUpdate);
		}
	}

	public void delete(EntityManager em, K id) {
		log.info("delete " + getEntityClass() + " : " + id);
		E entityToDelete = retrieve(em, id);
		if (entityToDelete == null)
			return;
		em.remove(entityToDelete);
	}

	@SuppressWarnings( { "unchecked", "unused" })
	private Class<E> getKeyClass() {
		return (Class<E>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	@SuppressWarnings("unchecked")
	private Class<E> getEntityClass() {
		return (Class<E>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[1];
	}

	private TypedQuery<E> getQuery(EntityManager em) {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<E> cq = cb.createQuery(getEntityClass());
		Root<E> emp = cq.from(getEntityClass());
		cq = cq.select(emp);
		return em.createQuery(cq);
	}
}
